# -*- coding: utf-8 -*-
import json
import logging
import math
import time
from ast import literal_eval
from datetime import date, timedelta
from collections import defaultdict

from odoo import SUPERUSER_ID, _, api, Command, fields, models
from odoo.addons.stock.models.stock_move import PROCUREMENT_PRIORITIES
from odoo.addons.web.controllers.utils import clean_action
from odoo.exceptions import UserError, ValidationError
from odoo.osv import expression
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, format_datetime, format_date, groupby
from odoo.tools.float_utils import float_compare, float_is_zero, float_round


class StockPickingTypeExt(models.Model):
    _inherit = 'stock.picking.type'

    code = fields.Selection(selection_add=[
        ('issue', 'Workshop Material Issue'), ('wmissue', 'Production Material Issue')
    ], ondelete={'issue': 'cascade', 'wmissue': 'cascade'})


class PickingExt(models.Model):
    _inherit = "stock.picking"

    workcenter_id = fields.Many2one(
        'mrp.workcenter', 'Work Center', check_company=True)
    sales_order_id = fields.Many2one(
        'sale.order', 'Sales Order')
    product_mfg_id = fields.Many2one('product.product', 'Manufactured Product', store=True)

    material_id = fields.Many2one('product.product', 'BOM Material')
    material_uom_id = fields.Many2one(
        'uom.uom', 'Material Unit of Measure', required=True
    )
    is_alt = fields.Boolean("Alternative", default=False)
    alt_material_id = fields.Many2one('product.product', 'Alternative Material')
    alt_material_uom_id = fields.Many2one(
        'uom.uom', 'Material Unit of Measure', required=True
    )
    quan_bom = fields.Float("BOM Quantity", digits='Product Unit of Measure', default=0)
    alt_quan_bom = fields.Float("Alternative Quantity", digits='Product Unit of Measure', default=0)
    m_qty = fields.Float("Material Quantity", digits='Product Unit of Measure', default=0)
    allowed_mrp_production_ids = fields.One2many('mrp.production', compute='_compute_allowed_mrp_production_ids')
    allowed_mrp_operation_ids = fields.One2many('stock.move', compute='_compute_allowed_mrp_operation_ids')
    move_ids_mrp_operation = fields.One2many(
        'stock.move', 'picking_id', string="Stock move",
        domain="[('id', 'in', allowed_mrp_operation_ids)]")
    move_ids_mrp_operation_count = fields.Integer(
        compute="_compute_move_ids_mrp_operation_count",
    )
    tot_qty = fields.Float("Total Quantity", digits='Product Unit of Measure', default=0,
                           compute="_compute_move_ids_mrp_operation_count")

    # @api.onchange('picking_type_id')
    # def _onchange_picking_type_id(self):
    #     if self.picking_type_code == 'issue':
    #         self.location_dest_id.readonly = True
    def _compute_move_ids_mrp_operation_count(self):
        self.move_ids_mrp_operation_count = 0
        if self.move_ids_mrp_operation:
            self.tot_qty = 0
            for m in self.move_ids_mrp_operation:
                self.move_ids_mrp_operation_count = self.move_ids_mrp_operation_count + 1
                self.tot_qty = self.tot_qty + m.quantity

    def write(self, vals):
        picking_type_code = vals.get('picking_type_code')
        if picking_type_code == "issue":
            if not vals.get('workcenter_id'):
                raise UserError(_("Please select a work center"))
        if picking_type_code == "wmissue":
            is_alt = vals.get('is_alt') | False
            if is_alt:
                alt_material_id = vals.get('alt_material_id')
                if not alt_material_id:
                    raise UserError(_("Please select an alternative material"))

                alt_material_uom_id = vals.get('alt_material_uom_id')
                alt_quan_bom = vals.get('alt_quan_bom') or 0
                if alt_quan_bom <= 0:
                    raise UserError(_("Please enter the BOM quantity for the alternative material"))
                moves = vals.get('move_ids_mrp_operation')
                if moves:
                    for m in moves:
                        m.update({
                            'alt_material_id': alt_material_id,
                            'alt_material_uom_id': alt_material_uom_id,
                            'alt_quan_bom': alt_quan_bom,
                            'quantity': m.quantity,
                            'quantity_done': m.quantity_done
                        })
            else:
                moves = vals.get('move_ids_mrp_operation')
                if moves:
                    for m in moves:
                        m.update({
                            'quantity': m.quantity,
                            'quantity_done': m.quantity_done
                        })
        logging.info(vals)
        res = super(PickingExt, self).write(vals)
        return res

    @api.depends('sales_order_id', 'product_mfg_id')
    def _compute_allowed_mrp_production_ids(self):
        if self.sales_order_id:
            so = self.sales_order_id.name
            mos = self.env['mrp.production'].search([
                ('origin', 'like', so), ('product_id', '=', self.product_mfg_id.id)], limit=9999)
            ids = []
            if mos:
                for m in mos:
                    ids.append(m.id)
                    ids = self._get_related_mos(m.name, ids)
            logging.info("ids=")
            logging.info(ids)
            self.allowed_mrp_production_ids = self.env['mrp.production'].search([('id', 'in', ids)], limit=9999)
            mids = []
            # if self.allowed_mrp_production_ids:
            # if self.material_id:
            # self.allowed_mrp_operation_ids = self.env["stock.move"].search(
            #    [('raw_material_production_id', 'in', ids)], limit=9999)
            # ,('product_id','=',self.material_id.id)

            # logging.info(self.allowed_mrp_operation_ids)

    @api.depends('allowed_mrp_production_ids', 'material_id')
    def _compute_allowed_mrp_operation_ids(self):
        logging.info('_compute_allowed_mrp_operation_ids')
        if self.material_id:
            logging.info(self.material_id)
            ids = []
            if self.allowed_mrp_production_ids:
                ids = self.allowed_mrp_production_ids.ids
            moves = self.env["stock.move"].search([('raw_material_production_id', 'in', ids)
                                                      , ('product_id', '=', self.material_id.id)
                                                      , ('state', 'not in', ('done', 'cancel'))], limit=9999)
            logging.info(moves)
            self.allowed_mrp_operation_ids = moves
            for m in moves:
                self.quan_bom = m.bom_line_id.product_qty
                self.material_uom_id = m.bom_line_id.product_uom_id
                break
        else:
            logging.info('material_id is null')
            # self.update({'allowed_mrp_operation_ids',False})
            self.allowed_mrp_operation_ids = False

    def _get_related_mos(self, origin, ids):
        # ids = []
        mos = self.env['mrp.production'].search([
            ('origin', 'like', origin), ], limit=9999)
        if mos:
            for m in mos:
                ids.append(m.id)
                ids = self._get_related_mos(m.name, ids)
        return ids

    @api.onchange('sales_order_id')
    def _onchange_sales_order_id(self):
        if self.sales_order_id:
            for i in self.sales_order_id.order_line:
                self.product_mfg_id = i.product_id.id
            so = self.sales_order_id.name
            ids = []
            ids = self._get_related_mos(so, ids)
            logging.info(ids)
            self._compute_allowed_mrp_production_ids()

    @api.onchange('material_id')
    def _onchange_material_id(self):
        if self.material_id:
            self._compute_allowed_mrp_operation_ids()

    @api.onchange('is_alt')
    def _onchange_is_alt(self):
        if not self.is_alt:
            self.update({'alt_material_id': False, 'alt_material_uom_id': False})

    @api.onchange('alt_material_id')
    def _onchange_alt_material_id(self):
        if self.alt_material_id:
            self.alt_material_uom_id = self.alt_material_id.uom_id
            self.alt_quan_bom = self.quan_bom

    def _recalc_alt_material(self):
        tot = 0
        for m in self.move_ids_mrp_operation:
            tot = tot + m.quantity
            if self.is_alt:
                m.alt_quan_done = m.quantity / self.quan_bom * self.alt_quan_bom
        if self.is_alt:
            self.m_qty = tot / self.quan_bom * self.alt_quan_bom
        else:
            self.m_qty = tot

    @api.onchange('material_id')
    def _onchange_move_ids_mrp_operation(self):
        self.move_ids_mrp_operation = False

    @api.onchange('move_ids_mrp_operation', 'alt_quan_bom', 'is_alt', 'alt_material_id')
    def _onchange_move_ids_mrp_operation(self):
        self._recalc_alt_material()

    @api.onchange('m_qty')
    def _onchange_m_qty(self):
        if self.m_qty:
            diff = 0
            icount = self.move_ids_mrp_operation_count
            if self.is_alt:
                tot_qty = self.tot_qty / self.quan_bom * self.alt_quan_bom  # 注意替代需要转换后才判断
                if self.m_qty != tot_qty:
                    diff = self.m_qty - tot_qty
                if icount > 0:
                    assign_diff = 0
                    for m in self.move_ids_mrp_operation:
                        alt_q = m.quantity / self.quan_bom * self.alt_quan_bom
                        tmp = round(alt_q / self.tot_qty, 2) * diff
                        assign_diff = assign_diff + tmp
                        m.alt_quan_done = alt_q + tmp
                        m.quantity = m.alt_quan_done / self.alt_quan_bom * self.quan_bom
                    last_diff = diff - assign_diff
                    for m in self.move_ids_mrp_operation:
                        m.alt_quan_done = alt_q + tmp
                        m.quantity = m.alt_quan_done / self.alt_quan_bom * self.quan_bom
                        break
            else:
                if self.m_qty != self.tot_qty:
                    diff = self.m_qty - self.tot_qty

                if icount > 0:
                    assign_diff = 0
                    for m in self.move_ids_mrp_operation:
                        tmp = round(m.quantity / self.tot_qty, 2) * diff
                        assign_diff = assign_diff + tmp
                        m.quantity = m.product_qty + tmp
                    last_diff = diff - assign_diff
                    for m in self.move_ids_mrp_operation:
                        m.quantity = m.quantity + last_diff
                        break

    def resetStockMove(self):
        for m in self.move_ids_mrp_operation:
            m.update({
                'quantity': m.ori_product_qty,
                'quantity_done': m.ori_product_qty,
                'product_id': m.ori_product_id,
                'product_uom': m.ori_product_uom,
                'product_uom_qty': m.ori_product_uom_qty,
                'unit_factor': m.ori_unit_factor,
                'partner_id': False,
                'picking_id': False,
                'alt_material_id': False,
                'alt_material_uom_id': False,
                'alt_quan_bom': False,
                'alt_quan_done': False
            })

    def unlink(self):
        picking_type_code = self.picking_type_code
        if picking_type_code == "wmissue":
            # 取消保留
            for m in self.move_ids_mrp_operation:
                m._do_unreserve()
            # 恢复原来的数据
            self.resetStockMove()
            return super(PickingExt, self).unlink()
        else:
            self.move_ids._action_cancel()
            self.with_context(prefetch_fields=False).move_ids.unlink()  # Checks if moves are not done
            return super(PickingExt, self).unlink()

    def action_cancel(self):
        picking_type_code = self.picking_type_code
        if picking_type_code == "wmissue":
            # 取消保留
            self.move_ids._do_unreserve()
            self.resetStockMove()
            super().action_cancel()
            return True
        else:
            super().action_cancel()
            return True

    def action_confirm(self):
        picking_type_code = self.picking_type_code
        if picking_type_code == "wmissue":
            # 取消保留
            for m in self.move_ids_mrp_operation:
                m._do_unreserve()

            is_alt = self.is_alt
            if is_alt:
                alt_material_id = self.alt_material_id
                if not alt_material_id:
                    raise UserError(_("Please select alternative material"))
                alt_material_uom_id = self.alt_material_uom_id
                alt_quan_bom = self.alt_quan_bom
                if alt_quan_bom <= 0:
                    raise UserError(_("Please enter the BOM quantity of the substitute material"))
                moves = self.move_ids_mrp_operation
                if moves:
                    for m in moves:
                        m.update({
                            'alt_material_id': alt_material_id,
                            'alt_material_uom_id': alt_material_uom_id,
                            'alt_quan_bom': alt_quan_bom,
                            'quantity': m.quantity,
                            'product_id': alt_material_id,
                            'product_uom': alt_material_uom_id,
                            'product_uom_qty': m.alt_quan_done
                        })
        return super(PickingExt, self).action_confirm()
